using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Text;
using System.Threading;
using CSUtils;
using UnityEngine;

namespace U3dUtils
{
    #if UNITY_EDITOR || UNITY_ANDROID || UNITY_IOS
    public class CLogCat : ILog
    {
        const string _tag = "CLogCat";
        public const string cIsUploadLogFile = "__isUploadLogFile_";
        const string tagg = "→";
        const char split01 = CString.cSplit01;
        public static bool isShowTestLog { get; private set; } = false;
        internal static CLogCat _instance;
        static int exPopupCount;
        public CParameter parameter = new CParameter();

        List<byte[]> logList = new List<byte[]>();
        bool isInitEnd;
        string deviceInfo;

        public static int mainThreadId { get; private set; }
        public string logCurrSaveDir { get { return CLogTxtSave.saveRootDir; } }
        public string currLogFilePath { get { return CLogTxtSave.currLogFilePath; } }

        CLogCat()
        {
        }

        public static CLogCat GetInstance()
        {
            if (_instance == null)
            {
                _instance = new CLogCat();
            }
            return _instance;
        }

        //        [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.SubsystemRegistration)]
        //        static void __Init(){
        //            GetInstance().Init();
        //        }
        //
        //        [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.AfterSceneLoad)]
        //        static void __Init2(){
        //            CLogCatMono.GetObj(GetInstance());
        //        }

        public void Init(bool isRealtimeSave = true, bool editorIsSaveLog = false, bool isSendLog = false)
        {
            if (Application.isPlaying == false && editorIsSaveLog == false)
            {
                return;
            }
            if (isInitEnd)
            {
                return;
            }
            bool isEditor = false;
            #if UNITY_EDITOR
            isEditor = true;
            #endif

            mainThreadId = Thread.CurrentThread.ManagedThreadId;
            isInitEnd = true;
            exPopupCount = 0;
            if (Application.isPlaying)
            {
                //CLogCatMono.GetObj(this);
            }
            deviceInfo = $"{Application.identifier}_{SystemInfo.deviceModel}_{SystemInfo.deviceName}";
            // #if !UNITY_IOS //&& CppLog
            // cpp_log_output.S_RegisterDelegateToCpp02();
            // #endif
            int limitLength = 80;
            if (deviceInfo.Length > limitLength)
            {
                deviceInfo = deviceInfo.Remove(limitLength, deviceInfo.Length - limitLength);
            }
            var _localSaveRootDir = "";
            var _midDir = "";
            if (Application.platform == RuntimePlatform.Android || Application.platform == RuntimePlatform.IPhonePlayer)
            {
                _localSaveRootDir = Application.persistentDataPath;
                _midDir = $"logList/currLogList/{CParameter.versionCodeStr}_{Application.version}";
            } else
            {
                #if UNITY_EDITOR
                _localSaveRootDir = Application.dataPath + "/../Library";
                #else
                _localSaveRootDir = Application.dataPath + "/..";
                #endif
                _midDir = "logList/currLogList/1250_4.4.1";
            }
            if (parameter.isSaveToLocal)
            {
                CLogTxtSave.Init(_localSaveRootDir, _midDir, isRealtimeSave);
            }
            CLog.Init(this);
            //FibMatrix.Logger.AppendTarget(new CLoggerTarget());

            CLog.i(_tag, "unityLog.注册启动.....");
            Application.logMessageReceivedThreaded += _logCallback;
        }

        void _logCallback(string logStr, string stackTrace, LogType type)
        {
            if (logStr.EndsWith(tagg))
            {
                return;
            }
            RefreshLogTime();
            _SendLog((byte)type, "", CTime.currTimeStr, logStr, stackTrace, null);
            if (type == LogType.Exception)
            {
                CheckErrTips(logStr + "/n" + stackTrace, true);
            }
        }

        public static void RefreshLogTime()
        {
            if (mainThreadId != Thread.CurrentThread.ManagedThreadId)
            {
                return;
            }
            if (CTime.frameCount == Time.frameCount)
            {
                return;
            }
            //return;
//            float timer = (float)RealTimer.elapsedSeconds;
//            int frameCount = Time.frameCount;
//            CTime.SetData(frameCount, timer, 1, 0.02f, timer, timer + "_" + frameCount);

            CTime.SetData(Time.frameCount, Time.time, Time.timeScale, Time.deltaTime, Time.realtimeSinceStartup, Time.realtimeSinceStartup + "_" + Time.frameCount);
            //Debug.Log($"{_tag}...RefreshLogTime().......timeScale={CTime.timeScale}....{CTime.currTimeStr}.....{CTime.deltaTime2}.");
        }

        public static void ClearAll()
        {
            CLogTxtSave.ClearAll();
        }

        public void Clear(bool isRemove = true)
        {
            CLog.i(_tag, "unityLog.注册关闭.........Application.logMessageReceivedThreaded -= _logCallback");
            Application.logMessageReceivedThreaded -= _logCallback;
            _instance = null;
            CLogTxtSave.ClearAll(isRemove);
        }

        public string GetCurrNewFilePath(string fileName)
        {
            return CLogTxtSave.GetCurrNewFilePath(fileName);
        }

        public void _SLog(byte type, string tag, string time, string logStr, string stackTrace, string colorStr)
        {
            if (tag == null)
            {
                tag = "null";
            }
            RefreshLogTime();
            if (string.IsNullOrEmpty(time))
            {
                time = CTime.currTimeStr;
            }
            if (logStr == null)
            {
                logStr = "null-log";
            }
            if (stackTrace == null)
            {
                stackTrace = "null";
            }
            _SendLog(type, tag, time, logStr, stackTrace, colorStr);
            UnityLog(type, $"{tag}.....{logStr}", stackTrace, colorStr);
        }

        public void _SendLog(byte type, string tag, string time, string logStr, string stackTrace, string colorStr)
        {
            try
            {
                CLogTxtSave.AddLog(type, tag, time, logStr, stackTrace, colorStr);
            } catch { }
        }

        void UnityLog(byte type, string msg, string stackTrace, string color)
        {
            //msg = msg.GetLogColor(out string color);
            if (color != null)
            {
                msg = msg.SetColor(color);
            }
            if (string.IsNullOrEmpty(stackTrace) == false)
            {
                msg = $"{msg}\n{stackTrace} {tagg}";
            } else
            {
                msg = $"({msg} {tagg}";
            }
            switch (type)
            {
                case 0:
                    Debug.LogError(msg);
                    break;
                case 2:
                    Debug.LogWarning(msg);
                    break;
                case 3:
                    Debug.Log(msg);
                    break;
                case 4:
                    CheckErrTips(msg + "/n" + stackTrace, true);
                    Debug.LogError(msg.SetColor("bb22ff") + tagg);
                    break;
                case 5:
                    //CheckErrTips(msg + "/n" + stackTrace, false);
                    Debug.LogError(msg.SetColor("9922ff") + tagg);
                    break;
            }
        }

        static void CheckErrTips(string message, bool isTips)
        {
            if (exPopupCount > 4)
            {
                return;
            }
            exPopupCount++;
            if (mainThreadId == Thread.CurrentThread.ManagedThreadId)
            {
                PlayerPrefs.SetInt(cIsUploadLogFile, 1);
            }
            if (isTips)
            {
                ShowTips(message);
            }
        }

        public static void ShowTips(string msg, string comfirm = "知道了", string title = "程序报错了")
        {
            const int limit = 600;
            if (msg.Length > limit)
            {
                msg = msg.Substring(0, limit);
            }
            if (CParameter.onExceptionTips == null)
            {
                #if UNITY_EDITOR
                //title = $"{title}({BuildInfo.Instance.getUnityBuildTime()}))";
                if (Thread.CurrentThread.ManagedThreadId == mainThreadId)
                {
                    // UnityEditor.EditorUtility.DisplayDialog(title, msg, comfirm);
                }
                #endif
            } else
            {
                CParameter.onExceptionTips.Invoke(title, msg, comfirm);
            }
        }

        /// <summary>
        /// 后台删除多余的log文件
        /// </summary>
        public static void BackDelectLogFile()
        {
            try
            {
                Thread thread = new Thread(delegate()
                {
                    CLogTxtSave.LimiteLogfileNumber();
                });
                thread.IsBackground = true;
                thread.Start();
            } catch { }
        }

        /// <summary>
        /// 配置参数
        /// </summary>
        public class CParameter
        {
            public static Action<string, string, string> onExceptionTips;
            public static string versionCodeStr;
            public string domainName = "ec2-52-82-11-149.cn-northwest-1.compute.amazonaws.com.cn";
            public bool isUseDomainName = false;
            public string ipV4 = "192.168.2.188";
            public bool isUseIPv6;
            public string ipV6 = "fe80::9105:fbf:dcbd:ae33";
            public bool isSendLog = false;
            /// <summary>
            /// 是否保存到本地
            /// </summary> 
            public bool isSaveToLocal = true;
        }
    }
    class CLogCatMono : MonoBehaviour
    {
        CLogCat logCat;
        float lastDelectTime;
        float lastShowMemoryTime;

        internal static CLogCatMono GetObj(CLogCat logCat)
        {
            if (_instance == null)
            {
                var go = GameObject.CreatePrimitive(PrimitiveType.Cube);
                go.name = "CLogCat.Mono";
                GameObject.DontDestroyOnLoad(go);
                _instance = go.AddComponent<CLogCatMono>();
                _instance.logCat = logCat;
            }
            return _instance;
        }

        static CLogCatMono _instance;

        string GetIp(string domainName)
        {
            IPHostEntry iPHostEntry = Dns.GetHostEntry(domainName);
            IPAddress[] ips = iPHostEntry.AddressList;
            for (int i = 0; i < ips.Length; i++)
            {
                Debug.Log($"域名解析ip_{i}_____{ips[i]}");
            }
            return ips[0].ToString();
        }

        void Awake()
        {
            //CMonoMonitor.RefreshLogTime();
            CMonoMonitor.RegisterAppQuitEvent("CLogCat", delegate()
            {
                logCat.Clear(false);
            });
        }

        void Start()
        {
            //CMonoMonitor.RefreshLogTime();
        }

        void Update()
        {
            if (logCat == null)
            {
                return;
            }
            if (logCat.parameter.isSaveToLocal)
            {
                if (lastDelectTime < Time.time)
                {
                    lastDelectTime = 120 + Time.time;
                    CLogCat.BackDelectLogFile();
                }
            }
        }
    }

    #else
    public class CLogCat : ILog
    {
        internal static CLogCat _instance;

        public void Init(bool isRealtimeSave = true, bool editorIsSaveLog = false, bool isSendLog = false)
        {
            CLog.Init(this);
            CLogTxtSave.Init(null, "CmdServer", true);
            FibMatrix.Logger.AppendTarget(new CLoggerTarget());
        }

        public static CLogCat GetInstance()
        {
            if (_instance == null)
            {
                _instance = new CLogCat();
            }
            return _instance;
        }

        static void _SLog(byte type, string tag, string time, string logStr, string stackTrace, string colorStr)
        {
            if (tag == null)
            {
                tag = "null";
            }
            if (string.IsNullOrEmpty(time))
            {
                time = CTime.currTimeStr;
            }
            if (logStr == null)
            {
                logStr = "null-log";
            }
            if (stackTrace == null)
            {
                stackTrace = "null";
            }
            try
            {
                CLogTxtSave.AddLog(type, tag, time, logStr, stackTrace, colorStr);
            } catch { }
        }

        void ILog._SLog(byte type, string tag, string time, string logStr, string stackTrace, string colorStr) => _SLog(type, tag, time, logStr, stackTrace, colorStr);

        public static void SaveAllLogData()
        {
            CLogTxtSave.SaveAllLogData();
        }
        public static void ClearAll()
        {
            CLogTxtSave.ClearAll();
        }
    }


    public class CLoggerTarget : ILoggerTarget
    {
        public CLoggerTarget()
        {
            targetType = LogTargetType.Runtime;
        }

        public void Debug(string message, object context, LogImportance importance)
        {
            var stackTrace = CLog.GetStackTrace("", null, 3);
            CLog.logObj._SLog(TLogType.Log, null, null, message + context, stackTrace, null);
        }

        public void Info(string message, object context, LogImportance importance)
        {
            var stackTrace = CLog.GetStackTrace("", null, 3);
            CLog.logObj._SLog(TLogType.Log, null, null, message + context, stackTrace, null);
        }

        public void Warning(string message, object context, LogImportance importance)
        {
            var stackTrace = CLog.GetStackTrace("", null, 3);
            CLog.logObj._SLog(TLogType.Warning, null, null, message + context, stackTrace, null);
        }

        public void Error(string message, string stackTrace, object context, LogImportance importance)
        {
            CLog.logObj._SLog(TLogType.Error, null, null, message + context, stackTrace, null);
        }

        public LogTargetType targetType { get; }
    }
    #endif
}
